﻿using System.Windows;
using System.Collections.Generic;
using AnimatingObjectsAlongAPath;
using System;
using System.Linq;
namespace DeepEarth.Toolkit.Geometry.Clipping
{
    // Fast. For anything else use an implementation of the Vatti algorithm
    // Recommended library: Clipper - http://sourceforge.net/scm/?type=svn&group_id=323752

    // This simplifed algorithm will only work on clipping boxes with points (left = -16848, top = -16848, right = 16848, bottom = 16848);
    public static class SimplifedSutherlandHodgman
    {
        public static List<Point> BoxClip (List<Point> points, double maxBound)
        {

            // http://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman
/* 
  List outputList = subjectPolygon;
  for (Edge clipEdge in clipPolygon) do
     List inputList = outputList;
     outputList.clear();
     Point S = inputList.last;
     for (Point E in inputList) do
        if (E inside clipEdge) then
           if (S not inside clipEdge) then
              outputList.add(ComputeIntersection(S,E,clipEdge));
           end if
           outputList.add(E);
        else if (S inside clipEdge) then
           outputList.add(ComputeIntersection(S,E,clipEdge));
        end if
        S = E;
     done
  done            
*/

            List<Edge> clipEdges = new List<Edge>() { Edge.left, Edge.bottom, Edge.right, Edge.top };
            List<Line> boundaries = Helpers.CreateClippingBoundaries(maxBound);

            List<Point> output = points;

            for (int i = 0; i < clipEdges.Count; i++)
            {
                if (!output.Any())
                {
                    break;
                }

                List<Point> input = output;
                output = new List<Point>();

                Point s = input[input.Count - 1];

                foreach (Point p in input)
                {
                    if (InsideClipEdge(p, clipEdges[i], maxBound))
                    {
                        if (!InsideClipEdge(s, clipEdges[i], maxBound))
                        {
                            Line currLine = new Line
                            {
                                X1 = s.X,
                                Y1 = s.Y,
                                X2 = p.X,
                                Y2 = p.Y
                            };

                            Point intersection = new Point();
                            Helpers.DoLinesIntersect(boundaries[i], currLine, ref intersection);
                            output.Add(intersection);
                        }
                        output.Add(p);
                    }
                    else if (InsideClipEdge(s, clipEdges[i], maxBound))
                    {
                        Line currLine = new Line
                        {
                            X1 = s.X,
                            Y1 = s.Y,
                            X2 = p.X,
                            Y2 = p.Y
                        };

                        Point intersection = new Point();
                        Helpers.DoLinesIntersect(boundaries[i], currLine, ref intersection);
                        output.Add(intersection);
                    }

                    s = p;
                }
            }


            if (output.Any())
            {
                // We're expecting first and last points the same which the algorighm doesn't ensure so check for this now
                Point first = output.First();

                if (first != null && first != output.Last())
                {
                    output.Add(new Point(first.X, first.Y));
                }
            }

            return output;
        }
       
        static bool InsideClipEdge(Point p, Edge edge, double maxBound)
        {
            switch (edge)
            {
                case Edge.left :
                    return p.X > -maxBound;
                case Edge.bottom:
                    return p.Y < maxBound;
                case Edge.right:
                    return p.X < maxBound;
                case Edge.top:
                    return p.Y > -maxBound;
            }

            throw new ArgumentException("How did this even compile?");
        }
    }

    public enum Edge
    {
        left, bottom, right, top
    }
}
